package com.allenkerr.config;

import com.google.common.collect.ImmutableMap;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.Map;

import static com.allenkerr.constant.RedisKey.REDIS_KEY_PERSON_CACHE;
import static com.allenkerr.constant.RedisKey.REDIS_KEY_PERSON_CACHE_EXPIRATION;

/**
 * Redis Config，适用于 SpringBoot 2.0
 *
 * @author 小柯
 * @date 2018/03/19
 */
@Configuration
@EnableCaching
public class RedisConfig {

  /**
   * RedisCacheManager 默认过期时间：1 hour
   *
   * @see RedisConfig#cacheManager(RedisConnectionFactory, RedisSerializer, RedisSerializer)
   */
  private static final long REDIS_CACHE_MANAGER_DEFAULT_EXPIRATION = 60 * 60L;

  @Bean
  public RedisSerializer<String> keySerializer() {
    return new StringRedisSerializer();
  }

  @Bean
  public RedisSerializer<Object> valueSerializer() {
    return new GenericJackson2JsonRedisSerializer();
  }

  @Bean
  public RedisTemplate redisTemplate(RedisTemplate redisTemplate,
                                     RedisSerializer keySerializer, RedisSerializer valueSerializer) {
    //set key serializer
    redisTemplate.setKeySerializer(keySerializer);
    redisTemplate.setHashKeySerializer(keySerializer);
    //set value serializer
    redisTemplate.setValueSerializer(valueSerializer);
    redisTemplate.setHashValueSerializer(valueSerializer);
    return redisTemplate;
  }

  @Bean
  public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,
                                   RedisSerializer<String> keySerializer, RedisSerializer<Object> valueSerializer) {
    //set the expire time (in seconds) for every redis key
    Map<String, RedisCacheConfiguration> cacheConfigs = ImmutableMap.<String, RedisCacheConfiguration>builder()
        .put(REDIS_KEY_PERSON_CACHE, cacheConfig(REDIS_KEY_PERSON_CACHE_EXPIRATION, keySerializer, valueSerializer))
        .build();
    //return cache manager
    return RedisCacheManager.RedisCacheManagerBuilder
        .fromConnectionFactory(redisConnectionFactory)
        //default expire time
        .cacheDefaults(cacheConfig(REDIS_CACHE_MANAGER_DEFAULT_EXPIRATION, keySerializer, valueSerializer))
        //expire time for every redis key
        .withInitialCacheConfigurations(cacheConfigs)
        .build();
  }

  private RedisCacheConfiguration cacheConfig(long seconds, RedisSerializer<String> keySerializer,
                                              RedisSerializer<Object> valueSerializer) {
    return RedisCacheConfiguration.defaultCacheConfig()
        //set the expire time (in seconds)
        .entryTtl(Duration.ofSeconds(seconds))
        //set the prefix of cache key
        .computePrefixWith(cacheName -> cacheName + ":")
        .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer))
        .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer));
  }

}
